from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains as AC
from selenium.webdriver.common.keys import Keys
from tool.file_path import *
from log.log import logger
import win32com.client
import win32clipboard as wc
import win32com
import time
import os


class Base:
    def __init__(self, driver):
        self.driver = driver
        self.actions=AC(self.driver)
        self.alert=self.driver.switch_to.alert

    def base_find_element(self, loc, timeout=10, poll_frequency=1):
        """
            001 定位单个元素
            定位到元素，返回元素对象，没定位到,超时异常
            isinstance判断类型,不是元祖提示非元祖类型,是元祖返回元素对象
        """
        if not isinstance(loc, tuple):
            logger.error('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
        else:
            try:
                logger.info(f"正在定位元素信息：定位方式->{loc[0]}, value值->{loc[1]}")
                ele = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                    lambda x: x.find_element(*loc))
                return ele
            except:
                logger.error("定位元素出现问题,检查属性是否存在或者写错")
                self.base_image()
                raise

    def base_find_elements(self, loc, timeout=10, poll_frequency=0.5):

        """
            002 定位多个元素
             定位到元素，返回元素列表对象，没定位到,超时异常
            isinstance判断类型,不是元祖提示非元祖类型,是元祖返回元素列表对象
        """
        if not isinstance(loc, tuple):
            logger.error('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
        else:
            try:
                logger.info(f"正在定位元素信息：定位方式->{loc[0]}, value值->{loc[1]}")
                eles = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                    lambda x: x.find_elements(*loc))
                return eles
            except:
                logger.error("返回null表示定位元素错误")
                return []  # 这里返回一个list

    def base_click(self, loc):
        """
            003 点击操作
        """
        self.base_find_element(loc).click()
        time.sleep(0.3)

    def base_input(self, loc, value):
        """
            004 输入操作
            该操作方式合并了clear清除文本的操作,无需在进行文本清除
        """
        el = self.base_find_element(loc)
        el.clear()
        el.send_keys(value)
        time.sleep(0.3)

    def base_input_keyboard(self, loc, value):
        """
            034 通过键盘将文本进行清除在输入的操作
            该操作方式合并了使用键盘清除文本的操作,无需在进行文本清除
        """
        el = self.base_find_element(loc)
        el.send_keys(Keys.CONTROL, "a")
        el.send_keys(Keys.DELETE)
        el.send_keys(value)    

    def base_element_is_exist(self, loc):
        """
            006 判断元素是否存在,如果存在返回True,不存在返回False
        """
        try:
            self.base_find_element(loc, timeout=2)
            return True
        except:
            return False

    def base_element_is_exists(self, loc):
        """
            007 判断元素是否存在,如果判断元素的长度为0,返回False,如果为1,返回True,如果为其他个数,打印元素个数,返回True
        """
        els = self.base_find_elements(loc)
        n = len(els)
        if n == 0:
            return False
        elif n == 1:
            logger.info("定位到元素的个数：1")
            return True
        else:
            logger.info(f"定位到元素的个数：{n}")
            return True
    
    def base_get_attribute(self, loc, name):
        """
            015 获取元素的属性值
        """
        try:
            element = self.base_find_element(loc)
            logger.info("成功获取元素属性")
            return element.get_attribute(name)
        except:
            logger.error(f"获取{name}属性失败，返回'' ")
            return ""

    def base_get_text(self, loc):
        """
            014 获取标签文本值
        """
        try:
            t = self.base_find_element(loc).text
            return t
        except:
            logger.error("获取text失败，返回空值 ")
            return ""

    def base_is_text_in_element(self, loc, text, timeout=10, poll_frequency=0.5):
        """
            010 判断元素是否存在指定文本text,如果存在返回True,不存在返回False
        """
        if not isinstance(loc, tuple):
            logger.info('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
        try:
            result = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.text_to_be_present_in_element(loc, text))
            return result
        except:
            return False
    
    def base_is_value_in_element(self, loc, value, timeout=10, poll_frequency=0.5):
        """
            011 判断元素的value属性是否包含预期值,存在返回True,不存在返回False
        """
        if not isinstance(loc, tuple):
            logger.info('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
        try:
            result = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.text_to_be_present_in_element_value(loc, value))
            return result
        except:
            return False

    def base_is_select(self, loc):
        """
            005 判断是否被选中,如果被选中,返回True/False
        """
        el = self.base_find_element(loc)
        r = el.is_selected()
        return r

    def base_get_title(self):
        """
            013 获取title
        """
        return self.driver.title

    def base_is_title(self, title, timeout=10, poll_frequency=0.5):
        """
            008 判断title是否等于预期值,等于返回True,不等于返回False
        """
        try:
            result = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.title_is(title))
            return result
        except:
            return False

    def base_is_title_contains(self, title, timeout=10, poll_frequency=0.5):
        """
            009 判断title是否包含预期值,包含就返回True,不包含就返回False
        """
        try:
            result = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.title_contains(title))
            return result
        except:
            return False

    def base_select_by_index(self, loc, index=0):
        """
            019 根据索引对select进行定位
        """
        element = self.base_find_element(loc)  # 定位select这一栏
        Select(element).select_by_index(index)

    def base_select_by_value(self, loc, value):
        """
            020 根据value值对select进行定位
        """
        element = self.base_find_element(loc)
        Select(element).select_by_value(value)

    def base_select_by_text(self, loc, text):
        """
            021 根据文本值对select进行定位
        """
        element = self.base_find_element(loc)
        Select(element).select_by_visible_text(text)

    def is_alert(self, timeout=10, poll_frequency=3):
        """
            012 判断是否弹窗,如果弹窗,打印捕捉到弹窗,是否非弹窗,则打印弹窗不存在
        """
        try:
            result = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.alert_is_present())
            logger.info("捕捉到弹窗")
            return result
        except:
            logger.error("弹窗不存在")
            return False

    def base_js_focus_element(self, loc):
        """
            016 页面需要滚动获得元素
        """
        target = self.base_find_element(loc)
        self.driver.execute_script("arguments[0].scrollIntoView();", target)

    def base_js_scroll_top(self):
        """
            017 滚动到顶部
        """
        js = "window.scrollTo(0,0)"
        self.driver.execute_script(js)

    def base_js_scroll_end(self, x=0):
        """
            018 滚动到底部（默认）
        """
        js = f"window.scrollTo({x},document.body.scrollHeight)"
        self.driver.execute_script(js)


    def base_left_double_click(self,loc):
        """
            双击左键
        """
        ele=self.base_find_element(loc)
        self.actions.double_click(ele).perform()

    def base_right_click(self,loc):
        """
            右键单击
        """
        ele=self.base_find_element(loc)
        self.actions.context_click(ele).perform()

    def base_move_mouse(self,loc):
        """
            鼠标悬浮
        """
        ele=self.base_find_element(loc)
        self.actions.move_to_element(ele).perform()

    
    def base_upload_file(self, path):
        """
            034 第二种上传方式
            操作无需使用第三方工具
        """
        shell = win32com.client.Dispatch("WScript.Shell")
        upload = shell.Sendkeys(f"{path}" + '{ENTER}' + '\n')
        time.sleep(1)
        return upload

    def base_switch_to_frame(self, loc):
        """
            035 切换到iframe

        """
        frame = self.base_find_element(loc)
        self.driver.switch_to.frame(frame)

    

    def base_switch_to_iframe(self, attr):
        """
            022 切换到frame框架,id_index_loc可以是id的属性,也可以是name的属性
            如果没有id跟name,可以根据frame的元素位置或者属性特征,使用find系列的方法
            driver.switch_to.frame(wd.find_element_by_tag_name("iframe"))
            进入iframe,操作结束之后,需要返回原页面进行操作需要退出当前iframe
            driver.switch_to.default_content()

        """
        try:
            if isinstance(attr, tuple):
                logger.info("传入的id_index_loc是元祖")
                ele = self.base_find_element(attr)
                self.driver.switch_to.frame(ele)
            else:
                logger.info("传入的id_index_loc是id或者name值")
                self.driver.switch_to.frame(attr)
        except Exception as info:
            logger.error(f"iframe切换失败，{info}")

    def base_switch_out_iframe(self):
        """
            036 切换到主文档

        """
        self.driver.switch_to.default_content()    

    def base_more_wicket_switch(self, loc):
        """
            023 多窗口切换(第一种方案)
        """
        current_handle = self.driver.current_window_handle
        self.base_click(loc) # 跳转
        handles = self.driver.window_handles
        if current_handle in handles:
            # 如果current_handle（当前句柄）在handles（所有句柄）里，就切换到倒数第一个句柄中
            self.driver.switch_to.window(handles[-1])
        else:
            logger.error("当前句柄不可与获取句柄一致")

    def base_switch_return_first(self):
        """
            回到之前的第一个句柄
            注意:当切换到第二个句柄，然后关掉时，需要切回到第一个才能进行操作
            （因为就算是关闭了第二个句柄，系统默认你还是在第二个句柄）
        """
        handles = self.driver.window_handles
        self.driver.switch_to_window(handles[0])


    def base_js_more_wicket_switch(self, tag, value):
        """
            024 多窗口切换(第二种方案,通过去除target属性,来解决切换的问题)
            tag是js中的Id,TagName等
            value为删除的属性
        """
        js = f'document.getElementBy{tag}.removeAttribute("{value}")'
        self.driver.execute_script(js)

    def base_win_alert(self,code,value):
        if isinstance(code,int):
            logger.error(f"输入的{code}不是int类型")
        else:
            try:
                if code==0:
                    self.alert.dismiss()
                elif code==1:
                    self.alert.accept()
                elif code==2:
                    self.alert.alert.send_keys(value)
                    self.alert.accept()
                else:
                    logger.error("输入的数字应该小于3")
            except Exception as info:
                logger.error(f"弹窗的下一步操作失败，{info}")

    def base_dispose_rich_text(self, tag, value1, value2):
        """
            031 通过js处理富文本
            tag是js中的Id,TagName等
            value1是tag与之对应的值
            value2为写入的富文本文字
        """
        js = f'document.getElementBy{tag}("{value1}").contentWindow.document.body.innerHTML="{value2}"'
        self.driver.execute_script(js)

    def base_dispose_data_control(self, tag, value1, value2):
        """
            032 通过js处理日期控件
            tag是js中的Id,TagName等
            value1是tag与之对应的值
            value2为删除的属性
        """
        js = f'document.getElementBy{tag}("{value1}").removeAttribute("{value2}")'
        self.driver.execute_script(js)

    def base_click_enter(self, loc):
        """
            026 模拟回车键
        """
        el = self.base_find_element(loc)
        el.send_keys(Keys.ENTER)
    
    
    def base_click_down(self, loc):
        """
            027 模拟向下按键
        """
        el = self.base_find_element(loc)
        el.send_keys(Keys.DOWN)

    def base_refresh(self):
        """
            028 刷新
        """
        self.driver.refresh()

    def base_back(self):
        """
            029 网页回退
        """
        self.driver.back()

    def base_get_image(self):
        """
            025 获取截图
        """
        self.driver.get_screenshot_as_file("../image/{}.png".format(time.strftime("%Y_%m_%d %H_%M_%S")))

    def base_image(self):
        file_time=time.strftime("%Y_%m_%d %H_%M_%S")
        self.driver.get_screenshot_as_file(f"{IMAGE_PATH}\{file_time}.png")
    
